#pragma once

#include <random>
#include <set>
#include <utility>
#include "type_traits.hpp"


// 基于梅森缠绕器算法的随机数生成器
inline auto& mt_engine() noexcept
{
    static thread_local std::mt19937_64 eng { std::random_device {}() };
    return eng;
}

inline auto randint() noexcept
{
    return mt_engine()();
}

/// @brief 随机范围[begin, end]
/// 从begin, end中随机一个整形数值,全闭区间
template <typename IntType>
inline IntType randint(IntType begin, IntType end) noexcept
{
    static_assert(std::is_integral<IntType>::value && sizeof(IntType) > 1, "argument must be an integer type");

    if(begin == end) return begin;

    return std::uniform_int_distribution<IntType>(begin, end)(mt_engine());
}

/// 从[0, __mod)中随机一个整形数值(结果<mod)
template <typename IntType>
inline IntType randint(IntType mod) noexcept
{
    assert(mod > 0);
    return randint<IntType>(0, mod - 1);
}

// 从容器里随机
// container 容器
// pred 成员是否参与随机的判断函数，对应成员调用pred返回true才可以参与随机
// 每个成员单独随机
// 成员的random_count（默认值为 1）表示成员随机次数
// 成员的probability表示 成员每次随机时的 万分之 概率
// 每次随机在概率之内即为选中(可重复选中)
template <typename Type, typename Container, typename Predicate>
inline auto random_if(const Container& container, Predicate pred) noexcept
{
    std::vector<Type> values;
    for (const auto& element : container)
    {
        if (pred(element))
        {
            int32_t random_count = 1;
            if constexpr (has_member(decltype(element), random_count))
            {
                random_count = element.random_count;
            }

            for (auto count = 0; count < random_count; ++count)
            {
                if (randint(10000) < element.probability)
                {
                    values.push_back(static_cast<Type>(element));
                }
            }
        }
    }
    return values;
}

// 从容器里随机
// container 容器
// 每个成员单独随机
// 成员的random_count（默认值为 1）表示成员随机次数
// 成员的probability表示 成员每次随机时的 万分之 概率
// 每次随机在概率之内即为选中(可重复选中)
template <typename Type, typename Container>
inline auto random(const Container& container) noexcept
{
    return random_if<Type>(container, [](auto&&) { return true; });
}

// 从容器里随机多个
// container 容器
// count 最多获得数量
// pred 成员是否参与随机的判断函数，对应成员调用pred返回true才可以参与随机
// 每个成员单独随机
// 成员的 weight 表示 成员每次随机时的 万分之 概率
// 每次随机在概率之内即为选中
template <typename Type, typename Container, typename Predicate>
inline auto multiple_random_if(const Container& container, size_t count, Predicate pred) noexcept
{
    std::vector<Type> values;
    for (const auto& element : container)
    {
        if (pred(element))
        {
            if (randint(10000) < element.weight)
            {
                values.push_back(static_cast<Type>(element));

                if(values.size() >= count)
                {
                    return values;
                }
            }
        }
    }
    return values;
}

// 从容器里随机多个
// container 容器
// count 最多获得数量
// 每个成员单独随机
// 成员的 weight 表示 成员每次随机时的 万分之 概率
// 每次随机在概率之内即为选中
template <typename Type, typename Container>
inline auto multiple_random(const Container& container, size_t count) noexcept
{
    return multiple_random_if<Type>(container, count, [](auto&&) { return true; });
}

// 从容器里随机一个成员
// begin 容器开始迭代器
// end 容器结束迭代器
// pred 成员是否参与随机的判断函数，对应成员调用pred返回true才可以参与随机
// 根据成员的权重（weight）随机一个成员
template <typename Iterator, typename Pred>
inline auto weighted_random_if(Iterator begin, Iterator end, Pred pred) noexcept
{
    int32_t total_weight = 0;
    for (auto iter = begin; iter != end; ++iter)
    {
        if (pred(std::as_const(*iter)))
        {
            if constexpr (std::is_pair<typename Iterator::value_type>::value)
            {
                total_weight += iter->second;
            }
            else
            {
                total_weight += iter->weight;
            }
        }
    }

    if (total_weight <= 0)
    {
        return end;
    }

    auto rand_num = randint(total_weight);
    for (auto iter = begin; iter != end; ++iter)
    {
        if (pred(std::as_const(*iter)))
        {
            if constexpr (std::is_pair<typename Iterator::value_type>::value)
            {
                if (rand_num < iter->second)
                {
                    return iter;
                }
                rand_num -= iter->second;
            }
            else
            {
                if (rand_num < iter->weight)
                {
                    return iter;
                }
                rand_num -= iter->weight;
            }
        }
    }
    return end;
}

// 从容器里随机一个成员
// container 容器
// pred 成员是否参与随机的判断函数，对应成员调用pred返回true才可以参与随机
// 根据成员的权重（weight）随机一个成员
template <typename Container, typename Pred>
inline auto weighted_random_if(const Container& container, Pred pred) noexcept -> const
typename Container::const_pointer
{
    auto iter = weighted_random_if(container.begin(), container.end(), pred);
    return iter == container.end() ? nullptr : std::addressof(*iter);
}

// 从容器里随机一个成员
// begin 容器开始迭代器
// end 容器结束迭代器
// 根据成员的权重（weight）随机一个成员
template <typename Iterator>
inline auto weighted_random(Iterator begin, Iterator end) noexcept
{
    return weighted_random_if(begin, end, [](auto&&) { return true; });
}

// 从容器里随机一个成员
// container 容器
// 根据成员的权重（weight）随机一个成员
template <typename Container>
inline auto weighted_random(const Container& container) noexcept -> const typename Container::const_pointer
{
    auto iter = weighted_random(container.begin(), container.end());
    return iter == container.end() ? nullptr : std::addressof(*iter);
}

// 从容器里随机多个成员，且各不相同
// container 容器
// count 需要得到的数量
// pred 成员是否参与随机的判断函数，对应成员调用pred返回true才可以参与随机
// 根据成员的权重（weight）随机一个成员
template <typename Type, typename Container, typename Predicate>
inline auto multiple_weighted_random_if(const Container& container, size_t count, Predicate pred) noexcept
{
    std::vector<Type> values;
    std::set<typename Container::const_pointer> contains;

    values.reserve(count);
    for (size_t i = 0 ; i < count; ++i)
    {
        const auto* value_ptr = weighted_random_if(container, [&pred, &contains](const auto& item){
            return pred(item) && contains.find(&item) == contains.end();
        });

        if (value_ptr == nullptr)
        {
            return values;
        }

        contains.insert(value_ptr);
        if constexpr (std::is_pair<std::remove_reference_t<decltype(*value_ptr)>>::value)
        {
            values.push_back(value_ptr->first);
        }
        else
        {
            values.push_back(static_cast<Type>(*value_ptr));
        }
    }
    return values;
}

// 从容器里随机多个成员，且各不相同
// container 容器
// count 需要得到的数量
// 根据成员的权重（weight）随机一个成员
template <typename Type, typename Container>
inline auto multiple_weighted_random(const Container& container, size_t count) noexcept
{
    return multiple_weighted_random_if<Type>(container, count, [](auto&&) { return true; });
}